package com.nc.mem.util;

import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.RandomAccess;

/**
 * Copied from guava, to be removed after JDK 8
 */
public final class Utils {

	private static class Partition<T> extends AbstractList<List<T>> {
		final List<T> list;
		final int size;

		Partition(final List<T> list, final int size) {
			this.list = list;
			this.size = size;
		}

		@Override
		public List<T> get(final int index) {
			final int listSize = size();
			checkElementIndex(index, listSize);
			final int start = index * size;
			final int end = Math.min(start + size, list.size());
			return list.subList(start, end);
		}

		@Override
		public boolean isEmpty() {
			return list.isEmpty();
		}

		@Override
		public int size() {
			int result = list.size() / size;
			if (result * size != list.size()) {
				result++;
			}
			return result;
		}
	}

	private static class RandomAccessPartition<T> extends Partition<T> implements RandomAccess {
		RandomAccessPartition(final List<T> list, final int size) {
			super(list, size);
		}
	}

	static final Iterator<Object> EMPTY_ITERATOR = new Iterator<Object>() {
		@Override
		public boolean hasNext() {
			return false;
		}

		@Override
		public Object next() {
			throw new NoSuchElementException();
		}

		@Override
		public void remove() {
			throw new UnsupportedOperationException();

		}
	};

	public static int checkElementIndex(final int index, final int size) {
		if (index < 0 || index >= size) {
			throw new IndexOutOfBoundsException();
		}
		return index;
	}

	public static <T> T checkNotNull(final T reference) {
		if (reference == null) {
			throw new NullPointerException();
		}
		return reference;
	}

	static void checkState(final boolean expression, final Object errorMessage) {
		if (!expression) {
			throw new IllegalStateException(String.valueOf(errorMessage));
		}
	}

	public static <T> Iterable<T> concat(final Iterable<? extends Iterable<? extends T>> inputs) {

		return new Iterable<T>() {
			@Override
			public Iterator<T> iterator() {
				return concat(iterators(inputs));
			}
		};
	}

	@SuppressWarnings("unchecked")
	public static <T> Iterable<T> concat(final Iterable<? extends T> a, final Iterable<? extends T> b) {
		if (a == null || b == null) {
			throw new IllegalArgumentException();
		}
		return concat(Arrays.asList(a, b));
	}

	static <T> Iterator<T> concat(final Iterator<? extends Iterator<? extends T>> inputs) {
		checkNotNull(inputs);
		return new Iterator<T>() {
			@SuppressWarnings("unchecked")
			Iterator<? extends T> current = (Iterator<? extends T>) EMPTY_ITERATOR;
			Iterator<? extends T> removeFrom;

			@Override
			public boolean hasNext() {
				boolean currentHasNext;

				while (!(currentHasNext = checkNotNull(current).hasNext()) && inputs.hasNext()) {
					current = inputs.next();
				}
				return currentHasNext;
			}

			@Override
			public T next() {
				if (!hasNext()) {
					throw new NoSuchElementException();
				}
				removeFrom = current;
				return current.next();
			}

			@Override
			public void remove() {
				checkState(removeFrom != null, "no calls to next() since last call to remove()");
				removeFrom.remove();
				removeFrom = null;
			}
		};
	}

	private static <T> Iterator<Iterator<? extends T>> iterators(final Iterable<? extends Iterable<? extends T>> iterables) {
		final Iterator<? extends Iterable<? extends T>> iterableIterator = iterables.iterator();
		return new Iterator<Iterator<? extends T>>() {
			@Override
			public boolean hasNext() {
				return iterableIterator.hasNext();
			}

			@Override
			public Iterator<? extends T> next() {
				return iterableIterator.next().iterator();
			}

			@Override
			public void remove() {
				throw new UnsupportedOperationException();

			}
		};
	}

	@SuppressWarnings({ "rawtypes", "unchecked" })
	public static <E> ArrayList<E> newArrayList(final Iterable<? extends E> elements) {
		checkNotNull(elements);
		return elements instanceof Collection ? new ArrayList<E>((Collection) elements) : newArrayList(elements.iterator());
	}

	public static <E> ArrayList<E> newArrayList(final Iterator<? extends E> elements) {
		checkNotNull(elements); // for GWT
		final ArrayList<E> list = new ArrayList<E>();
		while (elements.hasNext()) {
			list.add(elements.next());
		}
		return list;
	}

	public static <T> List<List<T>> partition(final List<T> list, final int size) {
		if (list == null || size <= 0) {
			throw new IllegalArgumentException();
		}
		return list instanceof RandomAccess ? new RandomAccessPartition<T>(list, size) : new Partition<T>(list, size);
	}

}
